#pragma once

#include <iostream>
#include <queue>
#include <pthread.h>
#include <unistd.h>

using namespace std;

template <class T> 
// 阻塞队列
class blockQueen
{
    static const int defalutunum = 20;

public:
    //构造函数
    blockQueen(int maxcap = defalutunum) : maxcap_(maxcap)
    {
        pthread_mutex_init(&mutex_, nullptr); //初始化锁
        pthread_cond_init(&c_cond_, nullptr);
        pthread_cond_init(&p_cond_, nullptr);

    }

    //消费
    T pop()
    {
        //加锁
        pthread_mutex_lock(&mutex_);
        //为啥要判断加在加锁和解锁之间呢？
        // 因为判断资源调度是否满足，也是在访问临界资源
        // 判断临界资源是否就绪，是通过在临界资源内部判断的
        while(q_.size() == 0)
        {   
            pthread_cond_wait(&c_cond_, &mutex_);
            //线程等待时，会自动释放锁，因为被唤醒而返回时，会重新持有锁，继续往后执行

        }
        //取出队列中的第一个元素
        T out = q_.front();
        //头删
        q_.pop();

        //if(q_.size()<low_water_) pthread_cond_signal(&p_cond_);
        //唤醒生产者
        pthread_cond_signal(&p_cond_);
        //解锁
        pthread_mutex_unlock(&mutex_);
        return out;
    }

    //生产
    void push(const T &in)
    {
        pthread_mutex_lock(&mutex_);
        while(q_.size() == maxcap_)
        {
            // 如果进程wait时，被误唤醒（伪唤醒）时，会怎么样呢？
            // 
            //调用的时候，自动释放锁，线程被唤醒的时候，直接被唤醒
            pthread_cond_wait(&p_cond_, &mutex_);
        }
        //1.队列，尾插数据
        q_.push(in);
        // if(q_.size()>high_water_) pthread_cond_signal(&c_cond_);
        //唤醒消费者
        pthread_cond_signal(&c_cond_);
        pthread_mutex_unlock(&mutex_);
    }

    ~blockQueen()
    {
        pthread_mutex_destroy(&mutex_);
        pthread_cond_destroy(&c_cond_);
        pthread_cond_destroy(&p_cond_);

    }

private:
    std::queue<T> q_;        
    // 共享资源
    // q被当做整体来使用，q只有一份，加锁
    // 但是共享资源也可以被看做多份
    int mincap_ ;
    int maxcap_; // 极值

    pthread_mutex_t mutex_;  // 定义一把锁
    pthread_cond_t c_cond_;  //定义一个消费条件变量
    pthread_cond_t p_cond_;  //定义生产的条件变量
    //标志位
    // int low_water_;
    // int high_water_;
};